--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 65e08a9d41ed4d0a4e222d4afbe678a47cb901ca
Parents : c727abe
Author : Mark Qvist <mark@unsigned.io>
Date : 2025-11-27T13:57:16+01:00
Cleanup unneeded dependencies
Changes
30 files changed, 6 insertions(+), 1857 deletions(-)
Diff
diff --git a/Makefile b/Makefile
index b74a4b52..7b08d25b 100644
--- a/Makefile
+++ b/Makefile
@@ -40,8 +40,10 @@ preparewheel:
pyclean .
$(MAKE) -C sbapp cleanrns
-build_wheel:
- python3 setup.py sdist bdist_wheel
+compile_wheel:
+ python3 setup.py bdist_wheel
+
+build_wheel: remove_symlinks compile_wheel create_symlinks
build_win_exe:
python -m PyInstaller sideband.spec --noconfirm
diff --git a/sbapp/plyer/__init__.py b/sbapp/plyer/__init__.py
index 9bfb1909..9859034b 100644
--- a/sbapp/plyer/__init__.py
+++ b/sbapp/plyer/__init__.py
@@ -5,7 +5,7 @@ Plyer
'''
__all__ = (
- 'accelerometer', 'audio', 'barometer', 'battery', 'bluetooth',
+ 'accelerometer', 'barometer', 'battery', 'bluetooth',
'brightness', 'call', 'camera', 'compass', 'cpu', 'email', 'filechooser',
'flash', 'gps', 'gravity', 'gyroscope', 'humidity', 'irblaster',
'keystore', 'light', 'notification', 'orientation', 'processors',
@@ -29,9 +29,6 @@ accelerometer = Proxy('accelerometer', facades.Accelerometer)
#: Keyring proxy to :class::`plyer.facades.Keystore`
keystore = Proxy('keystore', facades.Keystore)
-#: Audio proxy to :class:`plyer.facades.Audio`
-audio = Proxy('audio', facades.Audio)
-
#: Barometer proxy to :class:`plyer.facades.Barometer`
barometer = Proxy('barometer', facades.Barometer)
diff --git a/sbapp/plyer/facades/__init__.py b/sbapp/plyer/facades/__init__.py
index 81a0f631..88f39853 100644
--- a/sbapp/plyer/facades/__init__.py
+++ b/sbapp/plyer/facades/__init__.py
@@ -6,7 +6,7 @@ Interface of all the features available.
'''
-__all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera',
+__all__ = ('Accelerometer', 'Barometer', 'Battery', 'Call', 'Camera',
'Compass', 'Email', 'FileChooser', 'GPS', 'Gravity', 'Gyroscope',
'IrBlaster', 'Light', 'Orientation', 'Notification', 'Proximity',
'Sms', 'TTS', 'UniqueID', 'Vibrator', 'Wifi', 'Flash', 'CPU',
@@ -17,7 +17,6 @@ __all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera',
import RNS
if RNS.vendor.platformutils.is_android():
from plyer.facades.accelerometer import Accelerometer
- from plyer.facades.audio import Audio
from plyer.facades.barometer import Barometer
from plyer.facades.battery import Battery
from plyer.facades.call import Call
@@ -53,7 +52,6 @@ if RNS.vendor.platformutils.is_android():
from plyer.facades.devicename import DeviceName
else:
from sbapp.plyer.facades.accelerometer import Accelerometer
- from sbapp.plyer.facades.audio import Audio
from sbapp.plyer.facades.barometer import Barometer
from sbapp.plyer.facades.battery import Battery
from sbapp.plyer.facades.call import Call
diff --git a/sbapp/plyer/facades/audio.py b/sbapp/plyer/facades/audio.py
deleted file mode 100644
index 03940379..00000000
--- a/sbapp/plyer/facades/audio.py
+++ /dev/null
@@ -1,104 +0,0 @@
-'''
-Audio
-=====
-
-The :class:`Audio` is used for recording audio.
-
-Default path for recording is set in platform implementation.
-
-.. note::
- On Android the `RECORD_AUDIO`, `WAKE_LOCK` permissions are needed.
-
-Simple Examples
----------------
-
-To get the file path::
-
- >>> audio.file_path
- '/sdcard/testrecorder.3gp'
-
-To set the file path::
-
- >>> import os
- >>> current_list = os.listdir('.')
- ['/sdcard/testrecorder.3gp', '/sdcard/testrecorder1.3gp',
- '/sdcard/testrecorder2.3gp', '/sdcard/testrecorder3.3gp']
- >>> file_path = current_list[2]
- >>> audio.file_path = file_path
-
-To start recording::
-
- >>> from plyer import audio
- >>> audio.start()
-
-To stop recording::
-
- >>> audio.stop()
-
-To play recording::
-
- >>> audio.play()
-
-Supported Platforms
--------------------
-Android, Windows, macOS
-
-'''
-
-
-class Audio:
- '''
- Audio facade.
- '''
-
- state = 'ready'
- _file_path = ''
-
- def __init__(self, file_path=None):
- super().__init__()
- self._file_path = file_path or self._file_path
-
- def start(self):
- '''
- Start record.
- '''
- self._start()
- self.state = 'recording'
-
- def stop(self):
- '''
- Stop record.
- '''
- self._stop()
- self.state = 'ready'
-
- def play(self):
- '''
- Play current recording.
- '''
- self._play()
- self.state = 'playing'
-
- @property
- def file_path(self):
- return self._file_path
-
- @file_path.setter
- def file_path(self, location):
- '''
- Location of the recording.
- '''
- assert isinstance(location, str), 'Location must be string or unicode'
- self._file_path = location
-
- # private
-
- def _start(self):
- raise IOError("JUICE")
- raise NotImplementedError()
-
- def _stop(self):
- raise NotImplementedError()
-
- def _play(self):
- raise NotImplementedError()
diff --git a/sbapp/plyer/platforms/android/audio.py b/sbapp/plyer/platforms/android/audio.py
deleted file mode 100644
index 33a0a0d4..00000000
--- a/sbapp/plyer/platforms/android/audio.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import time
-import threading
-from jnius import autoclass
-
-from plyer.facades.audio import Audio
-
-# Recorder Classes
-MediaRecorder = autoclass('android.media.MediaRecorder')
-AudioSource = autoclass('android.media.MediaRecorder$AudioSource')
-OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat')
-AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder')
-
-# Player Classes
-MediaPlayer = autoclass('android.media.MediaPlayer')
-
-
-class AndroidAudio(Audio):
- '''Audio for android.
-
- For recording audio we use MediaRecorder Android class.
- For playing audio we use MediaPlayer Android class.
- '''
-
- def __init__(self, file_path=None):
- default_path = None
- super().__init__(file_path or default_path)
-
- self._recorder = None
- self._player = None
- self._check_thread = None
- self._finished_callback = None
- self._format = "opus"
- self.is_playing = False
-
- def _check_playback(self):
- while self._player and self._player.isPlaying():
- time.sleep(0.25)
-
- self.is_playing = False
-
- if self._finished_callback and callable(self._finished_callback):
- self._check_thread = None
- self._finished_callback(self)
-
-
- def _start(self):
- self._recorder = MediaRecorder()
- if self._format == "aac":
- self._recorder.setAudioSource(AudioSource.DEFAULT)
- self._recorder.setAudioSamplingRate(48000)
- self._recorder.setAudioEncodingBitRate(64000)
- self._recorder.setAudioChannels(1)
- self._recorder.setOutputFormat(OutputFormat.MPEG_4)
- self._recorder.setAudioEncoder(AudioEncoder.AAC)
-
- else:
- self._recorder.setAudioSource(AudioSource.DEFAULT)
- self._recorder.setAudioSamplingRate(48000)
- self._recorder.setAudioEncodingBitRate(12000)
- self._recorder.setAudioChannels(1)
- self._recorder.setOutputFormat(OutputFormat.OGG)
- self._recorder.setAudioEncoder(AudioEncoder.OPUS)
-
- self._recorder.setOutputFile(self.file_path)
-
- self._recorder.prepare()
- self._recorder.start()
-
- def _stop(self):
- if self._recorder:
- try:
- self._recorder.stop()
- self._recorder.release()
- except Exception as e:
- print("Could not stop recording: "+str(e))
-
- self._recorder = None
-
- if self._player:
- try:
- self._player.stop()
- self._player.release()
- except Exception as e:
- print("Could not stop playback: "+str(e))
-
- self._player = None
-
- self.is_playing = False
-
- def _play(self):
- self._player = MediaPlayer()
- self._player.setDataSource(self.file_path)
- self._player.prepare()
- self._player.start()
- self.is_playing = True
-
- self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
- self._check_thread.start()
-
- def reload(self):
- self._stop()
-
- def playing(self):
- return self.is_playing
-
-
-def instance():
- return AndroidAudio()
diff --git a/sbapp/plyer/platforms/ios/__init__.py b/sbapp/plyer/platforms/ios/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/sbapp/plyer/platforms/ios/accelerometer.py b/sbapp/plyer/platforms/ios/accelerometer.py
deleted file mode 100644
index 0f61f2b9..00000000
--- a/sbapp/plyer/platforms/ios/accelerometer.py
+++ /dev/null
@@ -1,34 +0,0 @@
-'''
-iOS accelerometer
------------------
-
-Taken from: http://pyobjus.readthedocs.org/en/latest/pyobjus_ios.html \
- #accessing-accelerometer
-'''
-
-from sbapp.plyer.facades import Accelerometer
-from pyobjus import autoclass
-
-
-class IosAccelerometer(Accelerometer):
-
- def __init__(self):
- super().__init__()
- self.bridge = autoclass('bridge').alloc().init()
- self.bridge.motionManager.setAccelerometerUpdateInterval_(0.1)
-
- def _enable(self):
- self.bridge.startAccelerometer()
-
- def _disable(self):
- self.bridge.stopAccelerometer()
-
- def _get_acceleration(self):
- return (
- self.bridge.ac_x,
- self.bridge.ac_y,
- self.bridge.ac_z)
-
-
-def instance():
- return IosAccelerometer()
diff --git a/sbapp/plyer/platforms/ios/barometer.py b/sbapp/plyer/platforms/ios/barometer.py
deleted file mode 100644
index 9ec8d6e5..00000000
--- a/sbapp/plyer/platforms/ios/barometer.py
+++ /dev/null
@@ -1,31 +0,0 @@
-'''
-iOS Barometer
--------------
-'''
-
-from sbapp.plyer.facades import Barometer
-from pyobjus import autoclass
-
-
-class iOSBarometer(Barometer):
-
- def __init__(self):
- super().__init__()
- self.bridge = autoclass('bridge').alloc().init()
-
- def _enable(self):
- self.bridge.startRelativeAltitude()
-
- def _disable(self):
- self.bridge.stopRelativeAltitude()
-
- def _get_pressure(self):
- '''
- 1 kPa = 10 hPa
- '''
- return (
- self.bridge.pressure * 10)
-
-
-def instance():
- return iOSBarometer()
diff --git a/sbapp/plyer/platforms/ios/battery.py b/sbapp/plyer/platforms/ios/battery.py
deleted file mode 100644
index d7fa5d81..00000000
--- a/sbapp/plyer/platforms/ios/battery.py
+++ /dev/null
@@ -1,47 +0,0 @@
-'''
-Module of iOS API for plyer.battery.
-'''
-
-from pyobjus import autoclass
-from pyobjus.dylib_manager import load_framework
-from sbapp.plyer.facades import Battery
-
-load_framework('/System/Library/Frameworks/UIKit.framework')
-UIDevice = autoclass('UIDevice')
-
-
-class IOSBattery(Battery):
- '''
- Implementation of iOS battery API.
- '''
-
- def __init__(self):
- super().__init__()
- self.device = UIDevice.currentDevice()
-
- def _get_state(self):
- status = {"isCharging": None, "percentage": None}
-
- if not self.device.batteryMonitoringEnabled:
- self.device.setBatteryMonitoringEnabled_(True)
-
- if self.device.batteryState == 0:
- is_charging = None
- elif self.device.batteryState == 2:
- is_charging = True
- else:
- is_charging = False
-
- percentage = self.device.batteryLevel * 100.
-
- status['isCharging'] = is_charging
- status['percentage'] = percentage
-
- return status
-
-
-def instance():
- '''
- Instance for facade proxy.
- '''
- return IOSBattery()
diff --git a/sbapp/plyer/platforms/ios/brightness.py b/sbapp/plyer/platforms/ios/brightness.py
deleted file mode 100644
index f4a70f4e..00000000
--- a/sbapp/plyer/platforms/ios/brightness.py
+++ /dev/null
@@ -1,27 +0,0 @@
-'''
-iOS Brightness
---------------
-'''
-
-from pyobjus import autoclass
-from sbapp.plyer.facades import Brightness
-from pyobjus.dylib_manager import load_framework
-
-load_framework('/System/Library/Frameworks/UIKit.framework')
-UIScreen = autoclass('UIScreen')
-
-
-class iOSBrightness(Brightness):
-
- def __init__(self):
- self.screen = UIScreen.mainScreen()
-
- def _current_level(self):
- return self.screen.brightness * 100
-
- def set_level(self, level):
- self.screen.brightness = level / 100
-
-
-def instance():
- return iOSBrightness()
diff --git a/sbapp/plyer/platforms/ios/call.py b/sbapp/plyer/platforms/ios/call.py
deleted file mode 100644
index 9999d6aa..00000000
--- a/sbapp/plyer/platforms/ios/call.py
+++ /dev/null
@@ -1,29 +0,0 @@
-'''
-IOS Call
-----------
-'''
-
-from sbapp.plyer.facades import Call
-from pyobjus import autoclass, objc_str
-
-NSURL = autoclass('NSURL')
-NSString = autoclass('NSString')
-UIApplication = autoclass('UIApplication')
-
-
-class IOSCall(Call):
-
- def _makecall(self, **kwargs):
- tel = kwargs.get('tel')
- url = "tel://" + tel
- nsurl = NSURL.alloc().initWithString_(objc_str(url))
-
- UIApplication.sharedApplication().openURL_(nsurl)
-
- def _dialcall(self, **kwargs):
- pass
- # Not possible, Access not provided by iPhone SDK
-
-
-def instance():
- return IOSCall()
diff --git a/sbapp/plyer/platforms/ios/camera.py b/sbapp/plyer/platforms/ios/camera.py
deleted file mode 100644
index a61bd96c..00000000
--- a/sbapp/plyer/platforms/ios/camera.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from os import remove
-from sbapp.plyer.facades import Camera
-
-from sbapp.plyer.utils import reify
-
-
-class iOSCamera(Camera):
-
- @reify
- def photos(self):
- # pyPhotoLibrary is a ios recipe/module that
- # interacts with the gallery and the camera on ios.
- from photolibrary import PhotosLibrary
- return PhotosLibrary()
-
- def _take_picture(self, on_complete, filename=None):
- assert on_complete is not None
- self.on_complete = on_complete
- self.filename = filename
- photos = self.photos
-
- if not photos.isCameraAvailable():
- # no camera hardware
- return False
-
- photos.bind(on_image_captured=self.capture_callback)
- self._capture_filename = filename
- photos.capture_image(filename)
- return True
-
- def capture_callback(self, photolibrary):
- # Image was chosen
-
- # unbind
- self.photos.unbind(on_image_captured=self.capture_callback)
-
- if self.on_complete(self.filename):
- self._remove(self.filename)
-
- def _take_video(self, on_complete, filename=None):
- assert on_complete is not None
- raise NotImplementedError
-
- def _remove(self, fn):
- try:
- remove(fn)
- except OSError:
- print('Could not remove photo!')
-
-
-def instance():
- return iOSCamera()
diff --git a/sbapp/plyer/platforms/ios/compass.py b/sbapp/plyer/platforms/ios/compass.py
deleted file mode 100644
index a5c865fb..00000000
--- a/sbapp/plyer/platforms/ios/compass.py
+++ /dev/null
@@ -1,43 +0,0 @@
-'''
-iOS Compass
------------
-'''
-
-from sbapp.plyer.facades import Compass
-from pyobjus import autoclass
-
-
-class IosCompass(Compass):
-
- def __init__(self):
- super().__init__()
- self.bridge = autoclass('bridge').alloc().init()
- self.bridge.motionManager.setMagnetometerUpdateInterval_(0.1)
- self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
-
- def _enable(self):
- self.bridge.startMagnetometer()
- self.bridge.startDeviceMotionWithReferenceFrame()
-
- def _disable(self):
- self.bridge.stopMagnetometer()
- self.bridge.stopDeviceMotion()
-
- def _get_orientation(self):
- return (
- self.bridge.mf_x,
- self.bridge.mf_y,
- self.bridge.mf_z)
-
- def _get_field_uncalib(self):
- return (
- self.bridge.mg_x,
- self.bridge.mg_y,
- self.bridge.mg_z,
- self.bridge.mg_x - self.bridge.mf_x,
- self.bridge.mg_y - self.bridge.mf_y,
- self.bridge.mg_z - self.bridge.mf_z)
-
-
-def instance():
- return IosCompass()
diff --git a/sbapp/plyer/platforms/ios/email.py b/sbapp/plyer/platforms/ios/email.py
deleted file mode 100644
index ec54860e..00000000
--- a/sbapp/plyer/platforms/ios/email.py
+++ /dev/null
@@ -1,52 +0,0 @@
-'''
-Module of iOS API for plyer.email.
-'''
-
-try:
- from urllib.parse import quote
-except ImportError:
- from urllib import quote
-
-from sbapp.plyer.facades import Email
-from pyobjus import autoclass, objc_str
-from pyobjus.dylib_manager import load_framework
-
-load_framework('/System/Library/Frameworks/UIKit.framework')
-
-NSURL = autoclass('NSURL')
-NSString = autoclass('NSString')
-UIApplication = autoclass('UIApplication')
-
-
-class IOSEmail(Email):
- '''
- Implementation of iOS battery API.
- '''
-
- def _send(self, **kwargs):
- recipient = kwargs.get('recipient')
- subject = kwargs.get('subject')
- text = kwargs.get('text')
-
- uri = "mailto:"
- if recipient:
- uri += str(recipient)
- if subject:
- uri += "?" if "?" not in uri else "&"
- uri += "subject="
- uri += quote(str(subject))
- if text:
- uri += "?" if "?" not in uri else "&"
- uri += "body="
- uri += quote(str(text))
-
- nsurl = NSURL.alloc().initWithString_(objc_str(uri))
-
- UIApplication.sharedApplication().openURL_(nsurl)
-
-
-def instance():
- '''
- Instance for facade proxy.
- '''
- return IOSEmail()
diff --git a/sbapp/plyer/platforms/ios/filechooser.py b/sbapp/plyer/platforms/ios/filechooser.py
deleted file mode 100644
index 0320d6c5..00000000
--- a/sbapp/plyer/platforms/ios/filechooser.py
+++ /dev/null
@@ -1,81 +0,0 @@
-'''
-IOS file chooser
---------------------
-
-This module houses the iOS implementation of the plyer FileChooser.
-
-.. versionadded:: 1.4.4
-'''
-
-from sbapp.plyer.facades import FileChooser
-from pyobjus import autoclass, protocol
-from pyobjus.dylib_manager import load_framework
-
-
-load_framework('/System/Library/Frameworks/Photos.framework')
-
-
-class IOSFileChooser(FileChooser):
- '''
- FileChooser implementation for IOS using
- the built-in file browser via UIImagePickerController.
-
- .. versionadded:: 1.4.0
- '''
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self._on_selection = None
-
- def _file_selection_dialog(self, *args, **kwargs):
- """
- Function called when action is required, A "mode" parameter specifies
- which and is one of "open", "save" or "dir".
- """
- self._on_selection = kwargs["on_selection"]
- if kwargs["mode"] == "open":
- self._open()
- else:
- raise NotImplementedError()
-
- def _get_picker(self):
- """
- Return an instantiated and configured UIImagePickerController.
- """
- picker = autoclass("UIImagePickerController")
- po = picker.alloc().init()
- po.sourceType = 0
- po.delegate = self
- return po
-
- def _open(self):
- """
- Launch the native iOS file browser. Upon selection, the
- `imagePickerController_didFinishPickingMediaWithInfo_` delegate is
- called where we close the file browser and handle the result.
- """
- picker = self._get_picker()
- UIApplication = autoclass('UIApplication')
- vc = UIApplication.sharedApplication().keyWindow.rootViewController()
- vc.presentViewController_animated_completion_(picker, True, None)
-
- @protocol('UIImagePickerControllerDelegate')
- def imagePickerController_didFinishPickingMediaWithInfo_(
- self, image_picker, frozen_dict):
- """
- Delegate which handles the result of the image selection process.
- """
- image_picker.dismissViewControllerAnimated_completion_(True, None)
-
- # Note: We need to call this Objective C class as there is currently
- # no way to call a non-class function via pyobjus. And here,
- # we have to use the `UIImagePNGRepresentation` to get the png
- # representation. For this, please ensure you are using an
- # appropriate version of kivy-ios.
- native_image_picker = autoclass("NativeImagePicker").alloc().init()
- path = native_image_picker.writeToPNG_(frozen_dict)
- self._on_selection([path.UTF8String()])
-
-
-def instance():
- return IOSFileChooser()
diff --git a/sbapp/plyer/platforms/ios/flash.py b/sbapp/plyer/platforms/ios/flash.py
deleted file mode 100644
index 17f59f12..00000000
--- a/sbapp/plyer/platforms/ios/flash.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# coding=utf-8
-"""
-Flash
------
-"""
-from sbapp.plyer.facades import Flash
-from pyobjus import autoclass
-
-NSString = autoclass("NSString")
-AVCaptureDevice = autoclass("AVCaptureDevice")
-AVMediaTypeVideo = NSString.alloc().initWithUTF8String_("vide")
-AVCaptureTorchModeOff = 0
-AVCaptureTorchModeOn = 1
-
-
-class IosFlash(Flash):
- _camera = None
-
- def _on(self):
- if self._camera is None:
- self._camera_open()
- if not self._camera:
- return
- self._camera.lockForConfiguration_(None)
- try:
- self._camera.setTorchMode(AVCaptureTorchModeOn)
- finally:
- self._camera.unlockForConfiguration()
-
- def _off(self):
- if not self._camera:
- return
- self._camera.lockForConfiguration_(None)
- try:
- self._camera.setTorchMode(AVCaptureTorchModeOff)
- finally:
- self._camera.unlockForConfiguration()
-
- def _release(self):
- pass
-
- def _camera_open(self):
- device = AVCaptureDevice.defaultDeviceWithMediaType_(AVMediaTypeVideo)
- if not device:
- return
- self._camera = device
-
-
-def instance():
- return IosFlash()
diff --git a/sbapp/plyer/platforms/ios/gps.py b/sbapp/plyer/platforms/ios/gps.py
deleted file mode 100644
index d7ee0a72..00000000
--- a/sbapp/plyer/platforms/ios/gps.py
+++ /dev/null
@@ -1,80 +0,0 @@
-'''
-iOS GPS
------------
-'''
-
-from pyobjus import autoclass, protocol
-from pyobjus.dylib_manager import load_framework
-from sbapp.plyer.facades import GPS
-
-load_framework('/System/Library/Frameworks/CoreLocation.framework')
-CLLocationManager = autoclass('CLLocationManager')
-
-
-class IosGPS(GPS):
- def _configure(self):
- if not hasattr(self, '_location_manager'):
- self._location_manager = CLLocationManager.alloc().init()
-
- def _start(self, **kwargs):
- self._location_manager.delegate = self
-
- self._location_manager.requestWhenInUseAuthorization()
- # NSLocationWhenInUseUsageDescription key must exist in Info.plist
- # file. When the authorization prompt is displayed your app goes
- # into pause mode and if your app doesn't support background mode
- # it will crash.
- self._location_manager.startUpdatingLocation()
-
- def _stop(self):
- self._location_manager.stopUpdatingLocation()
-
- @protocol('CLLocationManagerDelegate')
- def locationManager_didChangeAuthorizationStatus_(self, manager, status):
- if self.on_status:
- s_status = ''
- provider_status = ''
- provider = 'standard-ios-provider'
- if status == 0:
- provider_status = 'provider-disabled'
- s_status = 'notDetermined'
- elif status == 1:
- provider_status = 'provider-enabled'
- s_status = 'restricted'
- elif status == 2:
- provider_status = 'provider-disabled'
- s_status = 'denied'
- elif status == 3:
- provider_status = 'provider-enabled'
- s_status = 'authorizedAlways'
- elif status == 4:
- provider_status = 'provider-enabled'
- s_status = 'authorizedWhenInUse'
- self.on_status(provider_status, '{}: {}'.format(
- provider, s_status))
-
- @protocol('CLLocationManagerDelegate')
- def locationManager_didUpdateLocations_(self, manager, locations):
- location = manager.location
-
- description = location.description.UTF8String()
- split_description = description.split('<')[-1].split('>')[0].split(',')
-
- lat, lon = [float(coord) for coord in split_description]
- acc = float(description.split(' +/- ')[-1].split('m ')[0])
-
- speed = location.speed
- altitude = location.altitude
- course = location.course
-
- self.on_location(
- lat=lat,
- lon=lon,
- speed=speed,
- bearing=course,
- altitude=altitude,
- accuracy=acc)
-
-
-def instance():
- return IosGPS()
diff --git a/sbapp/plyer/platforms/ios/gravity.py b/sbapp/plyer/platforms/ios/gravity.py
deleted file mode 100644
index 9452bdac..00000000
--- a/sbapp/plyer/platforms/ios/gravity.py
+++ /dev/null
@@ -1,31 +0,0 @@
-'''
-iOS Gravity
------------
-
-'''
-
-from sbapp.plyer.facades import Gravity
-from pyobjus import autoclass
-
-
-class iOSGravity(Gravity):
-
- def __init__(self):
- self.bridge = autoclass('bridge').alloc().init()
- self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
-
- def _enable(self):
- self.bridge.startDeviceMotion()
-
- def _disable(self):
- self.bridge.stopDeviceMotion()
-
- def _get_gravity(self):
- return (
- self.bridge.g_x,
- self.bridge.g_y,
- self.bridge.g_z)
-
-
-def instance():
- return iOSGravity()
diff --git a/sbapp/plyer/platforms/ios/gyroscope.py b/sbapp/plyer/platforms/ios/gyroscope.py
deleted file mode 100644
index 367e1bfc..00000000
--- a/sbapp/plyer/platforms/ios/gyroscope.py
+++ /dev/null
@@ -1,55 +0,0 @@
-'''
-iOS Gyroscope
----------------------
-'''
-
-from sbapp.plyer.facades import Gyroscope
-from pyobjus import autoclass
-
-from pyobjus.dylib_manager import load_framework
-
-load_framework('/System/Library/Frameworks/UIKit.framework')
-UIDevice = autoclass('UIDevice')
-
-device = UIDevice.currentDevice()
-
-
-class IosGyroscope(Gyroscope):
-
- def __init__(self):
- super().__init__()
- self.bridge = autoclass('bridge').alloc().init()
-
- if int(device.systemVersion.UTF8String().split('.')[0]) <= 4:
- self.bridge.motionManager.setGyroscopeUpdateInterval_(0.1)
- else:
- self.bridge.motionManager.setGyroUpdateInterval_(0.1)
-
- self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
-
- def _enable(self):
- self.bridge.startGyroscope()
- self.bridge.startDeviceMotion()
-
- def _disable(self):
- self.bridge.stopGyroscope()
- self.bridge.stopDeviceMotion()
-
- def _get_orientation(self):
- return (
- self.bridge.rotation_rate_x,
- self.bridge.rotation_rate_y,
- self.bridge.rotation_rate_z)
-
- def _get_rotation_uncalib(self):
- return (
- self.bridge.gy_x,
- self.bridge.gy_y,
- self.bridge.gy_z,
- self.bridge.gy_x - self.bridge.rotation_rate_x,
- self.bridge.gy_y - self.bridge.rotation_rate_y,
- self.bridge.gy_z - self.bridge.rotation_rate_z)
-
-
-def instance():
- return IosGyroscope()
diff --git a/sbapp/plyer/platforms/ios/keystore.py b/sbapp/plyer/platforms/ios/keystore.py
deleted file mode 100644
index a18cde27..00000000
--- a/sbapp/plyer/platforms/ios/keystore.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from sbapp.plyer.facades import Keystore
-from pyobjus import autoclass, objc_str
-
-NSUserDefaults = autoclass('NSUserDefaults')
-
-
-class IosKeystore(Keystore):
-
- def _set_key(self, servicename, key, value, **kwargs):
- NSUserDefaults.standardUserDefaults().setObject_forKey_(
- objc_str(value), objc_str(key))
-
- def _get_key(self, servicename, key, **kwargs):
- ret = NSUserDefaults.standardUserDefaults().stringForKey_(
- objc_str(key))
- if ret is not None:
- return ret.UTF8String()
- else:
- return ret
-
-
-def instance():
- return IosKeystore()
diff --git a/sbapp/plyer/platforms/ios/maps.py b/sbapp/plyer/platforms/ios/maps.py
deleted file mode 100644
index dac01d5d..00000000
--- a/sbapp/plyer/platforms/ios/maps.py
+++ /dev/null
@@ -1,78 +0,0 @@
-'''
-Module of iOS API for plyer.maps.
-'''
-
-import webbrowser
-from sbapp.plyer.facades import Maps
-from urllib.parse import quote_plus
-
-
-class iOSMaps(Maps):
- '''
- Implementation of iOS Maps API.
- '''
-
- def _open_by_address(self, address, **kwargs):
- '''
- :param address: An address string that geolocation can understand.
- '''
-
- address = quote_plus(address, safe=',')
- maps_address = 'http://maps.apple.com/?address=' + address
-
- webbrowser.open(maps_address)
-
- def _open_by_lat_long(self, latitude, longitude, **kwargs):
- '''
- Open a coordinate span denoting a latitudinal delta and a
- longitudinal delta (similar to MKCoordinateSpan)
-
- :param name: (optional), will set the name of the dropped pin
- '''
-
- name = kwargs.get("name", "Selected Location")
- maps_address = 'http://maps.apple.com/?ll={},{}&q={}'.format(
- latitude, longitude, name)
-
- webbrowser.open(maps_address)
-
- def _search(self, query, **kwargs):
- '''
- :param query: A string that describes the search object (ex. "Pizza")
-
- :param latitude: (optional), narrow down query within area,
- MUST BE USED WITH LONGITUDE
-
- :param longitude: (optional), narrow down query within area,
- MUST BE USED WITH LATITUDE
- '''
-
- latitude = kwargs.get('latitude')
- longitude = kwargs.get('longitude')
-
- query = quote_plus(query, safe=',')
- maps_address = 'http://maps.apple.com/?q=' + query
-
- if latitude is not None and longitude is not None:
- maps_address += '&sll={},{}'.format(latitude, longitude)
-
- webbrowser.open(maps_address)
-
- def _route(self, saddr, daddr, **kwargs):
- '''
- :param saddr: can be given as 'address' or 'lat,long'
- :param daddr: can be given as 'address' or 'lat,long'
- '''
- saddr = quote_plus(saddr, safe=',')
- daddr = quote_plus(daddr, safe=',')
-
- maps_address = 'http://maps.apple.com/?saddr={}&daddr={}'.format(
- saddr, daddr)
- webbrowser.open(maps_address)
-
-
-def instance():
- '''
- Instance for facade proxy.
- '''
- return iOSMaps()
diff --git a/sbapp/plyer/platforms/ios/sms.py b/sbapp/plyer/platforms/ios/sms.py
deleted file mode 100644
index fc6b1b41..00000000
--- a/sbapp/plyer/platforms/ios/sms.py
+++ /dev/null
@@ -1,43 +0,0 @@
-'''
-IOS Sms
-----------
-'''
-
-from sbapp.plyer.facades import Sms
-from pyobjus import autoclass, objc_str
-from pyobjus.dylib_manager import load_framework
-
-NSURL = autoclass('NSURL')
-NSString = autoclass('NSString')
-UIApplication = autoclass('UIApplication')
-load_framework('/System/Library/Frameworks/MessageUI.framework')
-
-
-class IOSSms(Sms):
-
- def _send(self, **kwargs):
- '''
- This method provides sending messages to recipients.
-
- Expects 2 parameters in kwargs:
- - recipient: String type
- - message: String type
-
- Opens a message interface with recipient and message information.
- '''
- recipient = kwargs.get('recipient')
- message = kwargs.get('message')
- url = "sms:"
- if recipient:
- # Apple has not supported multiple recipients yet.
- url += str(recipient)
- if message:
- # Apple has to supported it yet.
- pass
-
- nsurl = NSURL.alloc().initWithString_(objc_str(url))
- UIApplication.sharedApplication().openURL_(nsurl)
-
-
-def instance():
- return IOSSms()
diff --git a/sbapp/plyer/platforms/ios/spatialorientation.py b/sbapp/plyer/platforms/ios/spatialorientation.py
deleted file mode 100644
index 7c6d9ccc..00000000
--- a/sbapp/plyer/platforms/ios/spatialorientation.py
+++ /dev/null
@@ -1,31 +0,0 @@
-'''
-iOS Spatial Orientation
------------------------
-
-'''
-
-from sbapp.plyer.facades import SpatialOrientation
-from pyobjus import autoclass
-
-
-class iOSSpatialOrientation(SpatialOrientation):
-
- def __init__(self):
- self.bridge = autoclass('bridge').alloc().init()
- self.bridge.motionManager.setDeviceMotionUpdateInterval_(0.1)
-
- def _enable_listener(self):
- self.bridge.startDeviceMotion()
-
- def _disable_listener(self):
- self.bridge.stopDeviceMotion()
-
- def _get_orientation(self):
- return (
- self.bridge.sp_yaw,
- self.bridge.sp_pitch,
- self.bridge.sp_roll)
-
-
-def instance():
- return iOSSpatialOrientation()
diff --git a/sbapp/plyer/platforms/ios/storagepath.py b/sbapp/plyer/platforms/ios/storagepath.py
deleted file mode 100644
index c69a083a..00000000
--- a/sbapp/plyer/platforms/ios/storagepath.py
+++ /dev/null
@@ -1,62 +0,0 @@
-'''
-iOS Storage Path
---------------------
-'''
-
-from sbapp.plyer.facades import StoragePath
-from pyobjus import autoclass
-import os
-
-NSFileManager = autoclass('NSFileManager')
-
-# Directory constants (NSSearchPathDirectory enumeration)
-NSApplicationDirectory = 1
-NSDocumentDirectory = 9
-NSDownloadsDirectory = 15
-NSMoviesDirectory = 17
-NSMusicDirectory = 18
-NSPicturesDirectory = 19
-
-
-class iOSStoragePath(StoragePath):
-
- def __init__(self):
- self.defaultManager = NSFileManager.defaultManager()
-
- def _get_home_dir(self):
- return os.path.expanduser('~/')
-
- def _get_external_storage_dir(self):
- return 'This feature is not implemented for this platform.'
-
- def _get_root_dir(self):
- return 'This feature is not implemented for this platform.'
-
- def _get_documents_dir(self):
- return self.defaultManager.URLsForDirectory_inDomains_(
- NSDocumentDirectory, 1).firstObject().absoluteString.UTF8String()
-
- def _get_downloads_dir(self):
- return self.defaultManager.URLsForDirectory_inDomains_(
- NSDownloadsDirectory, 1).firstObject().absoluteString.UTF8String()
-
- def _get_videos_dir(self):
- return self.defaultManager.URLsForDirectory_inDomains_(
- NSMoviesDirectory, 1).firstObject().absoluteString.UTF8String()
-
- def _get_music_dir(self):
- return self.defaultManager.URLsForDirectory_inDomains_(
- NSMusicDirectory, 1).firstObject().absoluteString.UTF8String()
-
- def _get_pictures_dir(self):
- return self.defaultManager.URLsForDirectory_inDomains_(
- NSPicturesDirectory, 1).firstObject().absoluteString.UTF8String()
-
- def _get_application_dir(self):
- return self.defaultManager.URLsForDirectory_inDomains_(
- NSApplicationDirectory, 1).firstObject().absoluteString.\
- UTF8String()
-
-
-def instance():
- return iOSStoragePath()
diff --git a/sbapp/plyer/platforms/ios/tts.py b/sbapp/plyer/platforms/ios/tts.py
deleted file mode 100644
index 510399d1..00000000
--- a/sbapp/plyer/platforms/ios/tts.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from pyobjus import autoclass, objc_str
-from pyobjus.dylib_manager import load_framework
-
-from sbapp.plyer.facades import TTS
-
-load_framework('/System/Library/Frameworks/AVFoundation.framework')
-AVSpeechUtterance = autoclass('AVSpeechUtterance')
-AVSpeechSynthesizer = autoclass('AVSpeechSynthesizer')
-AVSpeechSynthesisVoice = autoclass('AVSpeechSynthesisVoice')
-
-
-class iOSTextToSpeech(TTS):
- def __init__(self):
- super().__init__()
- self.synth = AVSpeechSynthesizer.alloc().init()
- self.voice = None
-
- def _set_locale(self, locale="en-US"):
- self.voice = AVSpeechSynthesisVoice.voiceWithLanguage_(
- objc_str(locale)
- )
-
- def _speak(self, **kwargs):
- message = kwargs.get('message')
-
- if not self.voice:
- self._set_locale()
-
- utterance = \
- AVSpeechUtterance.speechUtteranceWithString_(objc_str(message))
-
- utterance.voice = self.voice
- self.synth.speakUtterance_(utterance)
-
-
-def instance():
- return iOSTextToSpeech()
diff --git a/sbapp/plyer/platforms/ios/uniqueid.py b/sbapp/plyer/platforms/ios/uniqueid.py
deleted file mode 100644
index e9fa8154..00000000
--- a/sbapp/plyer/platforms/ios/uniqueid.py
+++ /dev/null
@@ -1,27 +0,0 @@
-'''
-Module of iOS API for plyer.uniqueid.
-'''
-
-from pyobjus import autoclass
-from pyobjus.dylib_manager import load_framework
-from sbapp.plyer.facades import UniqueID
-
-load_framework('/System/Library/Frameworks/UIKit.framework')
-UIDevice = autoclass('UIDevice')
-
-
-class IOSUniqueID(UniqueID):
- '''
- Implementation of iOS uniqueid API.
- '''
-
- def _get_uid(self):
- uuid = UIDevice.currentDevice().identifierForVendor.UUIDString()
- return uuid.UTF8String()
-
-
-def instance():
- '''
- Instance for facade proxy.
- '''
- return IOSUniqueID()
diff --git a/sbapp/plyer/platforms/ios/vibrator.py b/sbapp/plyer/platforms/ios/vibrator.py
deleted file mode 100644
index 05c12c53..00000000
--- a/sbapp/plyer/platforms/ios/vibrator.py
+++ /dev/null
@@ -1,43 +0,0 @@
-'''Implementation Vibrator for iOS.
-
-Install: Add AudioToolbox framework to your application.
-'''
-
-import ctypes
-from sbapp.plyer.facades import Vibrator
-
-
-class IosVibrator(Vibrator):
- '''iOS Vibrator class.
-
- iOS doesn't support any feature.
- All time, pattern, repetition are ignored.
- '''
-
- def __init__(self):
- super().__init__()
- try:
- self._func = ctypes.CDLL(None).AudioServicesPlaySystemSound
- except AttributeError:
- self._func = None
-
- def _vibrate(self, time=None, **kwargs):
- # kSystemSoundID_Vibrate is 0x00000FFF
- self._func(0xFFF)
-
- def _pattern(self, pattern=None, repeat=None, **kwargs):
- self._vibrate()
-
- def _exists(self, **kwargs):
- return self._func is not None
-
- def _cancel(self, **kwargs):
- pass
-
-
-def instance():
- '''Returns Vibrator
-
- :return: instance of class IosVibrator
- '''
- return IosVibrator()
diff --git a/sbapp/plyer/platforms/linux/audio.py b/sbapp/plyer/platforms/linux/audio.py
deleted file mode 100644
index 091e9e5c..00000000
--- a/sbapp/plyer/platforms/linux/audio.py
+++ /dev/null
@@ -1,139 +0,0 @@
-import time
-import threading
-import RNS
-import io
-from sbapp.plyer.facades.audio import Audio
-from ffpyplayer.player import MediaPlayer
-from sbapp.pyogg import OpusFile, OpusBufferedEncoder, OggOpusWriter
-import pyaudio
-
-class LinuxAudio(Audio):
-
- def __init__(self, file_path=None):
- default_path = None
- super().__init__(file_path or default_path)
-
- self._recorder = None
- self._player = None
- self._check_thread = None
- self._finished_callback = None
- self._loaded_path = None
- self.sound = None
- self.pa = None
- self.is_playing = False
- self.recorder = None
- self.should_record = False
-
- def _check_playback(self):
- run = True
- while run and self.sound != None and not self.sound.get_pause():
- time.sleep(0.25)
- if self.duration:
- pts = self.sound.get_pts()
- if pts > self.duration:
- run = False
-
- self.is_playing = False
-
- if self._finished_callback and callable(self._finished_callback):
- self._check_thread = None
- self._finished_callback(self)
-
- def _record_job(self):
- samples_per_second = self.default_rate;
- bytes_per_sample = 2; frame_duration_ms = 20
- opus_buffered_encoder = OpusBufferedEncoder()
- opus_buffered_encoder.set_application("voip")
- opus_buffered_encoder.set_sampling_frequency(samples_per_second)
- opus_buffered_encoder.set_channels(1)
- opus_buffered_encoder.set_frame_size(frame_duration_ms)
- ogg_opus_writer = OggOpusWriter(self._file_path, opus_buffered_encoder)
-
- frame_duration = frame_duration_ms/1000
- frame_size = int(frame_duration * samples_per_second)
- bytes_per_frame = frame_size*bytes_per_sample
-
- read_bytes = 0
- pcm_buf = b""
- should_continue = True
- while self.should_record and self.recorder:
- samples_available = self.recorder.get_read_available()
- bytes_available = samples_available*bytes_per_sample
- if bytes_available > 0:
- read_req = bytes_per_frame - len(pcm_buf)
- read_n = min(bytes_available, read_req)
- read_s = read_n//bytes_per_sample
- rb = self.recorder.read(read_s); read_bytes += len(rb)
- pcm_buf += rb
-
- if len(pcm_buf) == bytes_per_frame:
- ogg_opus_writer.write(memoryview(bytearray(pcm_buf)))
- # RNS.log("Wrote frame of "+str(len(pcm_buf))+", expected size "+str(bytes_per_frame))
- pcm_buf = b""
-
- # Finish up anything left in buffer
- time.sleep(frame_duration)
- samples_available = self.recorder.get_read_available()
- bytes_available = samples_available*bytes_per_sample
- if bytes_available > 0:
- read_req = bytes_per_frame - len(pcm_buf)
- read_n = min(bytes_available, read_req)
- read_s = read_n//bytes_per_sample
- rb = self.recorder.read(read_s); read_bytes += len(rb)
- pcm_buf += rb
-
- if len(pcm_buf) == bytes_per_frame:
- ogg_opus_writer.write(memoryview(bytearray(pcm_buf)))
- # RNS.log("Wrote frame of "+str(len(pcm_buf))+", expected size "+str(bytes_per_frame))
- pcm_buf = b""
-
- ogg_opus_writer.close()
- if self.recorder:
- self.recorder.close()
-
- def _start(self):
- self.should_record = True
- if self.pa == None:
- self.pa = pyaudio.PyAudio()
- self.default_input_device = self.pa.get_default_input_device_info()
- self.default_rate = 48000
- # self.default_rate = int(self.default_input_device["defaultSampleRate"])
- if self.recorder:
- self.recorder.close()
- self.recorder = None
- self.recorder = self.pa.open(self.default_rate, 1, pyaudio.paInt16, input=True)
- threading.Thread(target=self._record_job, daemon=True).start()
-
- def _stop(self):
- if self.should_record == True:
- self.should_record = False
-
- elif self.sound != None:
- self.sound.set_pause(True)
- self.sound.seek(0, relative=False)
- self.is_playing = False
-
- def _play(self):
- self.sound = MediaPlayer(self._file_path)
- self.metadata = self.sound.get_metadata()
- self.duration = self.metadata["duration"]
- if self.duration == None:
- time.sleep(0.15)
- self.metadata = self.sound.get_metadata()
- self.duration = self.metadata["duration"]
-
- self._loaded_path = self._file_path
- self.is_playing = True
-
- self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
- self._check_thread.start()
-
- def reload(self):
- self._loaded_path = None
-
- def playing(self):
- return self.is_playing
-
-
-def instance():
- return LinuxAudio()
diff --git a/sbapp/plyer/platforms/macosx/audio.py b/sbapp/plyer/platforms/macosx/audio.py
deleted file mode 100644
index 1f2069e0..00000000
--- a/sbapp/plyer/platforms/macosx/audio.py
+++ /dev/null
@@ -1,128 +0,0 @@
-from os.path import join
-
-from pyobjus import autoclass
-from pyobjus.dylib_manager import INCLUDE, load_framework
-
-from sbapp.plyer.facades import Audio
-from sbapp.plyer.platforms.macosx.storagepath import OSXStoragePath
-
-import threading
-
-load_framework(INCLUDE.Foundation)
-load_framework(INCLUDE.AVFoundation)
-
-AVAudioPlayer = autoclass("AVAudioPlayer")
-AVAudioRecorder = autoclass("AVAudioRecorder")
-AVAudioFormat = autoclass("AVAudioFormat")
-NSString = autoclass('NSString')
-NSURL = autoclass('NSURL')
-NSError = autoclass('NSError').alloc()
-
-
-class OSXAudio(Audio):
- def __init__(self, file_path=None):
- default_path = None
- super().__init__(file_path or default_path)
-
- self._recorder = None
- self._player = None
- self._current_file = None
-
- self._check_thread = None
- self._finished_callback = None
- self._loaded_path = None
- self.is_playing = False
- self.sound = None
- self.pa = None
- self.is_playing = False
- self.recorder = None
- self.should_record = False
-
- def _check_playback(self):
- while self._player and self._player.isPlaying:
- time.sleep(0.25)
-
- if self._finished_callback and callable(self._finished_callback):
- self._check_thread = None
- self._finished_callback(self)
-
- def _start(self):
- # Conversion of Python file path string to Objective-C NSString
- file_path_NSString = NSString.alloc()
- file_path_NSString = file_path_NSString.initWithUTF8String_(
- self._file_path
- )
-
- # Definition of Objective-C NSURL object for the output record file
- # specified by NSString file path
- file_NSURL = NSURL.alloc()
- file_NSURL = file_NSURL.initWithString_(file_path_NSString)
-
- # Internal audio file format specification
- af = AVAudioFormat.alloc()
- af = af.initWithCommonFormat_sampleRate_channels_interleaved_(
- 1, 44100.0, 1, True
- )
-
- # Audio recorder instance initialization with specified file NSURL
- # and audio file format
- self._recorder = AVAudioRecorder.alloc()
- self._recorder = self._recorder.initWithURL_format_error_(
- file_NSURL, af, NSError
- )
-
- if not self._recorder:
- raise Exception(NSError.code, NSError.domain)
-
- self._recorder.record()
-
- # Setting the currently recorded file as current file
- # for using it as a parameter in audio player
- self._current_file = file_NSURL
-
- def _stop(self):
- if self._recorder:
- self._recorder.stop()
- self._recorder = None
-
- if self._player:
- self._player.stop()
- self._player = None
-
- def _play(self):
- # Conversion of Python file path string to Objective-C NSString
- file_path_NSString = NSString.alloc()
- file_path_NSString = file_path_NSString.initWithUTF8String_(
- self._file_path
- )
-
- # Definition of Objective-C NSURL object for the output record file
- # specified by NSString file path
- file_NSURL = NSURL.alloc()
- file_NSURL = file_NSURL.initWithString_(file_path_NSString)
- self._current_file = file_NSURL
-
- # Audio player instance initialization with the file NSURL
- # of the last recorded audio file
- self._player = AVAudioPlayer.alloc()
- self._player = self._player.initWithContentsOfURL_error_(
- self._current_file, NSError
- )
-
- if not self._player:
- raise Exception(NSError.code, NSError.domain)
-
- self._player.play()
-
- self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
- self._check_thread.start()
-
- def reload(self):
- self._loaded_path = None
-
- def playing(self):
- return self.is_playing
-
-
-def instance():
- return OSXAudio()
diff --git a/sbapp/plyer/platforms/win/audio.py b/sbapp/plyer/platforms/win/audio.py
deleted file mode 100644
index c74d97d2..00000000
--- a/sbapp/plyer/platforms/win/audio.py
+++ /dev/null
@@ -1,413 +0,0 @@
-'''
-Documentation:
-http://docs.microsoft.com/en-us/windows/desktop/Multimedia
-
-.. versionadded:: 1.4.0
-'''
-
-from os.path import join
-
-from ctypes import windll
-from ctypes import (
- sizeof, c_void_p, c_ulonglong, c_ulong,
- c_wchar_p, byref, Structure, create_string_buffer
-)
-from ctypes.wintypes import DWORD, UINT
-
-from sbapp.plyer.facades import Audio
-from sbapp.plyer.platforms.win.storagepath import WinStoragePath
-
-# DWORD_PTR i.e. ULONG_PTR, 32/64bit
-ULONG_PTR = c_ulonglong if sizeof(c_void_p) == 8 else c_ulong
-
-# device specific symbols
-MCI_OPEN = 0x803
-MCI_OPEN_TYPE = 0x2000
-MCI_OPEN_ELEMENT = 512
-MCI_RECORD = 0x80F
-MCI_STOP = 0x808
-MCI_SAVE = 0x813
-MCI_PLAY = 0x806
-MCI_CLOSE = 0x804
-
-# recorder specific symbols
-MCI_FROM = 4
-MCI_TO = 8
-MCI_WAIT = 2
-MCI_SAVE_FILE = 256
-
-
-class MCI_OPEN_PARMS(Structure):
- '''
- Struct for MCI_OPEN message parameters.
-
- .. versionadded:: 1.4.0
- '''
-
- _fields_ = [
- ('mciOpenParms', ULONG_PTR),
- ('wDeviceID', UINT),
- ('lpstrDeviceType', c_wchar_p),
- ('lpstrElementName', c_wchar_p),
- ('lpstrAlias', c_wchar_p)
- ]
-
-
-class MCI_RECORD_PARMS(Structure):
- '''
- Struct for MCI_RECORD message parameters.
-
- http://docs.microsoft.com/en-us/windows/desktop/Multimedia/mci-record-parms
-
- .. versionadded:: 1.4.0
- '''
-
- _fields_ = [
- ('dwCallback', ULONG_PTR),
- ('dwFrom', DWORD),
- ('dwTo', DWORD)
- ]
-
-
-class MCI_SAVE_PARMS(Structure):
- '''
- Struct for MCI_SAVE message parameters.
-
- http://docs.microsoft.com/en-us/windows/desktop/Multimedia/mci-save-parms
-
- .. versionadded:: 1.4.0
- '''
-
- _fields_ = [
- ('dwCallback', ULONG_PTR),
- ('lpfilename', c_wchar_p)
- ]
-
-
-class MCI_PLAY_PARMS(Structure):
- '''
- Struct for MCI_PLAY message parameters.
-
- http://docs.microsoft.com/en-us/windows/desktop/Multimedia/mci-play-parms
-
- .. versionadded:: 1.4.0
- '''
-
- _fields_ = [
- ('dwCallback', ULONG_PTR),
- ('dwFrom', DWORD),
- ('dwTo', DWORD)
- ]
-
-
-def send_command(device, msg, flags, params):
- '''
- Generic mciSendCommandW() wrapper with error handler.
- All parameters are required as for mciSendCommandW().
- In case of no `params` passed, use `None`, that value
- won't be dereferenced.
-
- .. versionadded:: 1.4.0
- '''
-
- multimedia = windll.winmm
- send_command_w = multimedia.mciSendCommandW
- get_error = multimedia.mciGetErrorStringW
-
- # error text buffer
- # by API specification 128 is max, however the API sometimes
- # kind of does not respect the documented bounds and returns
- # more characters than buffer length...?!
- error_len = 128
-
- # big enough to prevent API accidentally segfaulting
- error_text = create_string_buffer(error_len * 2)
-
- # open a recording device with a new file
- error_code = send_command_w(
- device, # device ID
- msg,
- flags,
-
- # reference to parameters structure or original value
- # in case of params=False/0/None/...
- byref(params) if params else params
- )
-
- # handle error messages if any
- if error_code:
- # device did not open, raise an exception
- get_error(error_code, byref(error_text), error_len)
- error_text = error_text.raw.replace(b'\x00', b'').decode('utf-8')
-
- # either it can close already open device or it will fail because
- # the device is in non-closable state, but the end result is the same
- # and it makes no sense to parse MCI_CLOSE's error in this case
- send_command_w(device, MCI_CLOSE, 0, None)
- raise Exception(error_code, error_text)
-
- # return params struct because some commands write into it
- # to pass some values out of the local function scope
- return params
-
-
-class WinRecorder:
- '''
- Generic wrapper for MCI_RECORD handling the filenames and device closing
- in the same approach like it is used for other platforms.
-
- .. versionadded:: 1.4.0
- '''
-
- def __init__(self, device, filename):
- self._device = device
- self._filename = filename
-
- @property
- def device(self):
- '''
- Public property returning device ID.
-
- .. versionadded:: 1.4.0
- '''
- return self._device
-
- @property
- def filename(self):
- '''
- Public property returning filename for current recording.
-
- .. versionadded:: 1.4.0
- '''
- return self._filename
-
- def record(self):
- '''
- Start recording a WAV sound.
-
- .. versionadded:: 1.4.0
- '''
- send_command(
- device=self.device,
- msg=MCI_RECORD,
- flags=0,
- params=None
- )
-
- def stop(self):
- '''
- Stop recording and save the data to a file path
- self.filename. Wait until the file is written.
- Close the device afterwards.
-
- .. versionadded:: 1.4.0
- '''
-
- # stop the recording first
- send_command(
- device=self.device,
- msg=MCI_STOP,
- flags=MCI_WAIT,
- params=None
- )
-
- # choose filename for the WAV file
- save_params = MCI_SAVE_PARMS()
- save_params.lpfilename = self.filename
-
- # save the sound data to a file and wait
- # until it ends writing to the file
- send_command(
- device=self.device,
- msg=MCI_SAVE,
- flags=MCI_SAVE_FILE | MCI_WAIT,
- params=save_params
- )
-
- # close the recording device
- send_command(
- device=self.device,
- msg=MCI_CLOSE,
- flags=0,
- params=None
- )
-
-
-class WinPlayer:
- '''
- Generic wrapper for MCI_PLAY handling the device closing.
-
- .. versionadded:: 1.4.0
- '''
-
- def __init__(self, device):
- self._device = device
-
- @property
- def device(self):
- '''
- Public property returning device ID.
-
- .. versionadded:: 1.4.0
- '''
- return self._device
-
- def play(self):
- '''
- Start playing a WAV sound.
-
- .. versionadded:: 1.4.0
- '''
- play_params = MCI_PLAY_PARMS()
- play_params.dwFrom = 0
-
- send_command(
- device=self.device,
- msg=MCI_PLAY,
- flags=MCI_FROM,
- params=play_params
- )
-
- def stop(self):
- '''
- Stop playing a WAV sound and close the device.
-
- .. versionadded:: 1.4.0
- '''
- send_command(
- device=self.device,
- msg=MCI_STOP,
- flags=MCI_WAIT,
- params=None
- )
-
- # close the playing device
- send_command(
- device=self.device,
- msg=MCI_CLOSE,
- flags=0,
- params=None
- )
-
-
-class WinAudio(Audio):
- '''
- Windows implementation of audio recording and audio playing.
-
- .. versionadded:: 1.4.0
- '''
-
- def __init__(self, file_path=None):
- # default path unless specified otherwise
- default_path = join(
- WinStoragePath().get_music_dir(),
- 'audio.wav'
- )
- super().__init__(file_path or default_path)
-
- self._recorder = None
- self._player = None
- self._current_file = None
- self._check_thread = None
- self._finished_callback = None
- self._loaded_path = None
- self.is_playing = False
- self.sound = None
- self.pa = None
- self.is_playing = False
- self.recorder = None
- self.should_record = False
-
- def _start(self):
- '''
- Start recording a WAV sound in the background asynchronously.
-
- .. versionadded:: 1.4.0
- '''
-
- # clean everything before recording in case
- # there is a different device open
- self._stop()
-
- # create structure and set device parameters
- open_params = MCI_OPEN_PARMS()
- open_params.lpstrDeviceType = 'waveaudio'
- open_params.lpstrElementName = ''
-
- # open a new device for recording
- open_params = send_command(
- device=0, # device ID before opening
- msg=MCI_OPEN,
-
- # empty filename in lpstrElementName
- # device type in lpstrDeviceType
- flags=MCI_OPEN_ELEMENT | MCI_OPEN_TYPE,
- params=open_params
- )
-
- # get recorder with device id and path for saving
- self._recorder = WinRecorder(
- device=open_params.wDeviceID,
- filename=self._file_path
- )
- self._recorder.record()
-
- # Setting the currently recorded file as current file
- # for using it as a parameter in audio player
- self._current_file = self._recorder.filename
-
- def _stop(self):
- '''
- Stop recording or playing of a WAV sound.
-
- .. versionadded:: 1.4.0
- '''
-
- if self._recorder:
- self._recorder.stop()
- self._recorder = None
-
- if self._player:
- self._player.stop()
- self._player = None
-
- def _play(self):
- '''
- Play a WAV sound from a file. Prioritize latest recorded file before
- default file path from WinAudio.
-
- .. versionadded:: 1.4.0
- '''
-
- # create structure and set device parameters
- open_params = MCI_OPEN_PARMS()
- open_params.lpstrDeviceType = 'waveaudio'
- open_params.lpstrElementName = self._current_file or self._file_path
-
- # open a new device for playing
- open_params = send_command(
- device=0, # device ID before opening
- msg=MCI_OPEN,
-
- # existing filename in lpstrElementName
- # device type in lpstrDeviceType
- flags=MCI_OPEN_ELEMENT | MCI_OPEN_TYPE,
- params=open_params
- )
-
- # get recorder with device id and path for saving
- self._player = WinPlayer(device=open_params.wDeviceID)
- self._player.play()
-
- def reload(self):
- self._loaded_path = None
-
- def playing(self):
- return self.is_playing
-
-
-def instance():
- '''
- Instance for facade proxy.
- '''
- return WinAudio()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────